跳到主要内容

基于 RAP 实现前后端协同

· 阅读需 8 分钟
Random Image
图片与正文无关

在进行一些同时包含前端和后端工作的任务时,一般前端会对后端产生接口方面的依赖,以前的做法一般不外乎以下两种:

  1. 在接口准备好之前先做其他任务。

  2. 前后端约定接口格式,各种形式,口头,邮件,即时通讯,前端也会用各种简单或复杂的方式模拟后端接口,争取做到和后端协同开发。

这两种方式都不是特别好,第一种缺少协同,在之后的对接中可能接口不符合需求,还需要调整时,会由于依赖前端又必须去做其他事情以填补这个空白时间。而第二种虽然可以做到同步开发,但是前端需要额外的工作量去做 Mock 数据的事情,而后端在开发过程中对格式的变更也很难及时的通知到前端。所以为了在前后端分离这样的项目架构下,前后端更好的协同开发,我们需要更好的方法。

RAP 是什么

RAP 是阿里的技术同学利用业余时间开发的一款可视化接口管理工具,支持灵活的类型定义,数据模拟,数据校验等功能,自从面世以来,不仅在阿里厂内受到各个项目组的欢迎,也对外开源,并建立了免费的 SaaS 服务,服务于其他公司团队进行前后端协同配合。因为是开源的,所以也就支持私有部署。我们公司的私有部署地址如下:http://rap.host.tech,同学们可以自己注册账号体验一下。

和前后端协同类似,对于我们公司这样的微服务架构,各项目组之间的接口依赖也时有发生。使用 RAP 也能帮我们解决这方面的困扰。

怎么使用

RAP 提供了比较不错的 WIKI 文档和视频教程,入门不成问题,而且 RAP 很灵活,最开始我们可以帮其他项目组使用 RAP 管理接口,如果所有人都接受 RAP 的工作方式,那么可以各个项目组自己管理自己的项目接口。让前后端或者跨项目间使用相同的接口管理工具的好处是显而易见的,我们用统一的方式约定接口,变更接口,就能统一认知,减少沟通成本。

但是,在大家都认可 RAP 这种接口约定方式之后,怎样可以更进一步,把 RAP 规范化的加入到项目迭代开发的工作流中呢。 项目组在这方面做了一些尝试,以下是一些心得。

总体思路

我们希望前后端或者跨项目接口调用时,请求的都还是真实的接口路径,但是通过一些方法,可以按需进行拦截,转发到 Mock 接口服务器,而当真实接口准备好以后,代码不需要做任何改动,只需要修改配置,不对接口进行转发即可。我们在前后端的项目里以相同的思路实现了对前后端请求 Mock 的适配。

后端

后端 Mock 主要是为了跨项目接口合作,而且是不止一个项目,因此配置的格式需要是下面这个样子:

rap: {
host: 'http://rap.host.tech',
projects: [
{
id: 2,
host: 'http://crm2.host.tech',
apis: [
/api\/transfer-reject/
]
}
]
}

配置格式是很好理解的,就是当当前请求匹配配置接口时,将当前请求重定向到 RAP 服务器。这需要对请求进行重写。代码如下:

const oldRequest = global.request;
global.request = function*(...args) {
const rap = config.rap;
if (rap) {
const request = args[0];
const matchedProject = rap.projects.find(p => request.uri.startsWith(p.host));
if (matchedProject) {
const matchedApi = matchedProject.apis.find(a => a.test(request.uri));
if (matchedApi) {
request.uri = request.uri.replace(matchedProject.host, rap.host + `/mockjsdata/${matchedProject.id}`);
}
}
args[0] = request;
}
return yield oldRequest(...args);
}

这里我们尚未考虑相同接口 RESTAPI 不同动词的情况,等我们需要做这种区分时,会完善以上代码。

前端

前端和后端的思路类似,区别在于,前端除了可能直接调用其他各个项目的接口以外,最常见的是调用自己项目的后端接口。而自己项目的接口在调用时是不需要传域名的。

前端和后端还有一个很大的不同就是生产环境和开发环境的运行模式不一样,开发环境使用的是 Webpack 的 devServer 机制,开始的时候我们还担心在前端需要用两种思路来实现整合,但我们的前端小伙伴通过研究发现,其实最终在发起和转发请求这件事上调用的都是 fetch。

以下是配置代码:

rap: {
host: 'http://rap.host.tech',
projects: [
{
// 没有host,意思是Mock的是相同域名下的后端接口
id: 4,
apis: [
/api\/test/,
],
},
{
id: 5,
host: 'http://crms.host.com',
apis: [
/api\/frontend-developers/,
],
},
],
},

以下是重写 fetch 的相关代码:

const rap = config.rap;
const Project = rap && rap.projects ? rap.projects.find(project => !project.host) : {};

const originFetch = window.fetch;
window.fetch = (url, options, ...args) => {
let URL = url;
if (rap && rap.projects) {
const matchedProject = rap.projects.find(project => (URL.startsWith(project.host))) || Project;
const relativeURL = URL.replace(matchedProject.host, '');
const matchedApi = matchedProject.apis.find(api => api.test(relativeURL));
if (matchedApi) {
URL = `${rap.host}/mockjsdata/${matchedProject.id}${relativeURL}`;
options = {
...options,
credentials: 'omit',
};
}
}
return originFetch(URL, options, ...args);
}

同样,这里暂时也没有考虑相同接口不同动词的情况。

结束语

通过以上的方法对前后端以及跨项目接口配合进行规范之后,相信会在一定程度上提高工作效率,减少沟通成本和理解误差。目前我们也只是定义了这个接口配合流程,在实际工作中还会不断优化和调整,使其发挥出应有的作用。